
from sqlalchemy.orm import Session
from typing import Optional, List
from datetime import datetime
from src.database.manager import DBManager
from src.database.models import MonitoredUser, Video
from src.config import STATIC_ROOT_DIR, SPECIAL_ROOT_DIR_ORIGIN, SPECIAL_ROOT_DIR_TARGET

class VideoDAO:
    def __init__(self, session: Session = DBManager().get_session()):
        self.session = session

    def create(self, platform: str, video_id: str, user_id: int, 
               description: Optional[str] = None) -> Video:
        video = Video(
            platform=platform,
            video_id=video_id,
            user_id=user_id,
            description=description,
            created_at=datetime.utcnow()
        )
        self.session.add(video)
        self.session.commit()
        return video

    def get_by_id(self, video_id: int) -> Optional[Video]:
        return self.session.query(Video).filter(Video.id == video_id).first()

    def get_by_platform_id(self, platform: str, video_id: str) -> Optional[Video]:
        return self.session.query(Video)\
            .filter(Video.platform == platform, Video.video_id == video_id)\
            .first()
    
    def get_paginated(self, page: int = 1, page_size: int = 10):
        offset = (page - 1) * page_size
        return self.session.query(
            Video.id,
            Video.title, 
            Video.download_at,
            Video._local_path,
            Video.duration,
            MonitoredUser.username,
            MonitoredUser.category
        )\
            .join(MonitoredUser, Video.user_id == MonitoredUser.id, isouter=True)\
            .order_by(Video.created_at.desc())\
            .offset(offset)\
            .limit(page_size)\
            .all()

    def process_in_batches(self, callback, batch_size: int = 10):
        """
        Iterate through all videos in batches and process them using a callback function
        
        Args:
            callback: Function that takes a list of videos as parameter
            batch_size: Number of records to process in each batch
        """
        page = 1
        while True:
            batch = self.get_paginated(page=page, page_size=batch_size)
            if not batch:
                break
            callback(batch)
            page += 1

    def update_local_path(self, old_path: int, new_path: str) -> bool:
        rel_old_path = old_path.split(STATIC_ROOT_DIR, 1)[1].replace(SPECIAL_ROOT_DIR_ORIGIN, SPECIAL_ROOT_DIR_TARGET)
        rel_new_path = new_path.split(STATIC_ROOT_DIR, 1)[1].replace(SPECIAL_ROOT_DIR_ORIGIN, SPECIAL_ROOT_DIR_TARGET)
        video = self.session.query(Video)\
        .filter(Video._local_path == rel_old_path).first()
        if video:
            video.local_path = rel_new_path
            self.session.commit()
            return True
        return False
    
    def update_download_status(self, video_id: int, status: str, 
                             download_url: Optional[str] = None) -> bool:
        video = self.get_by_id(video_id)
        if not video:
            return False
            
        video.download_status = status
        if download_url:
            video.download_url = download_url
        video.download_at = datetime.utcnow()
        self.session.commit()
        return True

    def increment_retry(self, video_id: int, next_retry: datetime) -> bool:
        video = self.get_by_id(video_id)
        if not video:
            return False
            
        video.retry_count = Video.retry_count + 1
        video.next_retry_at = next_retry
        self.session.commit()
        return True

    def get_pending_downloads(self, max_retries: int = 3) -> List[Video]:
        return self.session.query(Video)\
            .filter(
                Video.download_status != 'completed',
                Video.retry_count < max_retries,
                (Video.next_retry_at == None) | 
                (Video.next_retry_at <= datetime.utcnow())
            )\
            .all()

    def delete(self, video_id: int) -> bool:
        video = self.get_by_id(video_id)
        if not video:
            return False
            
        self.session.delete(video)
        self.session.commit()
        return True

    def init_faiss_index(self):
        # Initialize FAISS index here
        pass